#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include<fcntl.h>
#include <unistd.h>

#include <time.h>
#include "agreement/agreement.h"
#include "agreement/obd_shanghai.h"
#include "DateTime.h"
#include "upload.h"

//#define  debug_log     0
#ifndef debug_log
#define  debug_log     1
#endif
//#undef   debug_log
#include <stdio.h>
#include <string.h>

//#ifdef debug_log
#if debug_log
#define pr_debug(fmt, ...) printf(fmt, ##__VA_ARGS__); fflush(stdout);
#else
#define pr_debug(fmt, ...) ;
#endif

#ifdef offsetof
#undef offsetof
#endif
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:    the pointer to the member.
 * @type:   the type of the container struct this is embedded in.
 * @member: the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({          \
    const typeof(((type *)0)->member)*__mptr = (ptr);    \
             (type *)((char *)__mptr - offsetof(type, member)); })

#if 0
static long get_file_size(const char* filename)
{
    long _size=0;
    FILE* fd = fopen(filename, "r");
    if(NULL == fd)
    {
        printf("file %s not exist! \n", filename);  fflush(stdout);
        return -1;
    }
    else
    {
        fseek(fd, 0, SEEK_END);
        _size = ftell(fd);
        fseek(fd, 0, SEEK_SET);
        return _size;
    }
}
#endif
//static char* load_file(const char* filename, long* const size)
//{
//    static char filebin[2*1024*1024]; // 2MB
//    long _size=0;
//    FILE* fd = fopen(filename, "r");
//    if(NULL == fd)
//    {
//        printf("file %s not exist! \n", filename);  fflush(stdout);
//        return NULL;
//    }
//    else
//    {
//        fseek(fd, 0, SEEK_END);
//        _size = ftell(fd);
//        fseek(fd, 0, SEEK_SET);
//        // read
//        memset(filebin, 0xFF, sizeof (filebin));
//        _size = fread(filebin, (size_t)_size, 1, fd);
//        if(_size<=0) return NULL;
//        *size = _size;
//        return filebin;
//    }
//}
static uint8_t obd_buf[1024*10];

static int save_to_file(const char* const filename, const uint32_t seek, const void* const data, const uint16_t len)
{
    long _size=0;
#if 0
    FILE* fd = fopen(filename, "r");
    if(NULL == fd)
    {
        printf("file %s not exist! \n", filename);  fflush(stdout);
        return NULL;
    }
    else
    {
        fseek(fd, 0, SEEK_END);
        _size = ftell(fd);
        fseek(fd, 0, SEEK_SET);
        // read
        memset(filebin, 0xFF, sizeof (filebin));
        _size = fread(filebin, (size_t)_size, 1, fd);
        if(_size<=0) return NULL;
        *size = _size;
        return filebin;
    }
#else
    FILE* fd = NULL;
    if(0==seek)
    {
        fd = fopen(filename, "w");
        fclose(fd);
    }
    fd = fopen(filename, "ab");
    if(NULL == fd)
    {
        printf("file %s not exist!..2 \n", filename);  fflush(stdout);
        return -1;
    }
    else
    {
        _size = fwrite(data, (size_t)len, 1, fd);
        if(_size<=0) return -2;
        fclose(fd);
    }
#endif
    return 0;
}
#if 0
static int copy_to_file(const char* const filesrc, const char* const filedes)
{
    long _size=0;
    long len=0;
    long _rize=0;
    size_t count=0;
    char filebin[512]; //
    FILE* fds = NULL;
    FILE* fdd = NULL;
    //printf("%s@%d fopen filesrc \n", __func__, __LINE__); fflush(stdout);
    fds = fopen(filesrc, "r");
    if(NULL == fds)
    {
        printf("filesrc %s not exist! \n", filesrc);  fflush(stdout);
        return -1;
    }
    //printf("%s@%d fopen filedes \n", __func__, __LINE__); fflush(stdout);
    fdd = fopen(filedes, "w");
    if(NULL == fdd)
    {
        printf("filedes %s not exist! \n", filedes);  fflush(stdout);
        fclose(fds);
        return -1;
    }
    //printf("%s@%d fseek \n", __func__, __LINE__); fflush(stdout);
    fseek(fds, 0, SEEK_END);
    _size = ftell(fds);
    fseek(fds, 0, SEEK_SET);
    //_size = total;
    _rize = 0;
    //printf("%s@%d for _size:%ld len:%ld _rize:%ld \n", __func__, __LINE__, _size, len, _rize); fflush(stdout);
    for(len=0; len<_size; len+=_rize)
    {
        _rize = _size - len;
        if(_rize>512) _rize=512;
        // read
        memset(filebin, 0xFF, sizeof (filebin));
        count=fread(filebin, (size_t)_rize, 1, fds);
        //printf("%s@%d fread _size:%ld len:%ld _rize:%ld count:%ld \n", __func__, __LINE__, _size, len, _rize, count); fflush(stdout);
        count=fwrite(filebin, (size_t)_rize, 1, fdd);
        //printf("%s@%d fwrite _size:%ld len:%ld _rize:%ld count:%ld \n", __func__, __LINE__, _size, len, _rize, count); fflush(stdout);
    }
    fclose(fds);
    fclose(fdd);
    return 0;
}
#endif

static int handle_request_userdef(const struct agreement_ofp* _agree_ofp, const struct general_pack_shanghai* const _pack, const int client, void(*csend)(const int sockfd, const void *buf, const uint16_t len))
{
    const struct shanghai_userdef *const msg = (const struct shanghai_userdef *const)(_pack->data);
    struct upload decode;
    static char filename[128];
    uint8_t buffer[sizeof(struct upload)];
    uint32_t _size=0;
    const char *suffix=NULL;
    uint32_t checksum;
    static uint32_t server_checksum;
    int len;
    int ret = STATUS_CLIENT_NULL;
    int fd = 0;
    len = upload_decode(&decode, msg->data, msg->_dsize);
    ret = STATUS_CLIENT_NULL;
    if(len<0)
    {
        printf("userdef decode error!\n"); fflush(stdout);
    }
    else
    {
//        printf("decode cmd : 0x%02X\n", decode.cmd); fflush(stdout);
//        printf("decode total : %d\n", decode.pack_total); fflush(stdout);
//        printf("decode index : %d\n", decode.pack_index); fflush(stdout);
//        printf("decode checksum : 0x%02X\n", decode.checksum); fflush(stdout);
//        printf("decode Model : %s\n", decode.Model); fflush(stdout);
//        printf("decode len : %d\n", decode.data_len); fflush(stdout);
//        printf("decode data : %s\n", decode.data); fflush(stdout);
//        printf("decode CRC : 0x%02X\n", decode.CRC); fflush(stdout);
        switch (decode.cmd)
        {
            case UPLOAD_LOGIN:  // 登录
                printf("userdef Login\n"); fflush(stdout);
                break;
            case UPLOAD_PUSH:   // 推送固件
                printf("userdef Push\n"); fflush(stdout);
                checksum = crc_form_file(fw_path, obd_buf, sizeof(obd_buf) );
                printf("Push checksum: 0x%04X 0x%04X\n", decode.checksum, checksum); fflush(stdout);
                if(checksum!=decode.checksum)
                {
                    ret = STATUS_CLIENT_PUSH;
                }
                break;
            case UPLOAD_QUERY_CFG:    // 查询
                printf("userdef Config\n"); fflush(stdout);
                printf("userdef Config: total:%d seek:%d\n", decode.pack_total, decode.pack_index); fflush(stdout);
                printf("userdef filename: %s\n", decode.data); fflush(stdout);
                if( ((decode.pack_total<<1)==decode.pack_index) && (decode.data_len>strlen("./*.bin")+1) ) // update
                {
                    decode.pack_index = 0;
                    decode.down_len = FRAME_DATA_LEN;
                    if(decode.down_len>decode.pack_total) decode.down_len=decode.pack_total;
                    decode.cmd = UPLOAD_DOWNLOAD;
                    memset(filename, 0, sizeof (filename));
                    memcpy(filename, decode.data, strlen((const char *)decode.data));
                    decode.data_len = strlen((const char *)decode.data)+1;
                    //_load = upload_init(UPLOAD_LOGIN, 0, 0, 0x12345678, "OBD1234567890ABCDEF", "Hello", 5);
                    memset(buffer, 0, sizeof (buffer));
                    len = upload_encode(&decode, buffer, sizeof (buffer));   // user def
                    len = _agree_ofp->encode(buffer, len, obd_buf, sizeof (obd_buf)); // encode
                    if(NULL != csend) csend(client, obd_buf, len); // 发送数据到客户端
                    ret = STATUS_CLIENT_DOWN;
                    //mkdir("./upload");
                    //rename(filename, "./upload/Download.dat");
                }
                else  ret = STATUS_CLIENT_DONE;
                break;
            case UPLOAD_DOWNLOAD:   // 下载
                printf("userdef DownLoad: total:%d seek:%d size:%d\n", decode.pack_total, decode.pack_index, decode.data_len); fflush(stdout);
                if( decode.data_len==decode.down_len ) // update
                {
                    //save_to_file(filename, decode.pack_index, decode.data, decode.down_len);
                    save_to_file("./upload/Download.dat", decode.pack_index, decode.data, decode.down_len);
                    decode.pack_index = decode.pack_index + decode.down_len;
                    _size = decode.pack_total - decode.pack_index;
                    if(_size>FRAME_DATA_LEN) _size = FRAME_DATA_LEN;
                    if(0==_size)  // done
                    {
                        ret = STATUS_CLIENT_DONE;
                        memset(buffer, 0, sizeof (buffer));
                        //snprintf((char*)buffer, sizeof (filename)-1, "./upload/%s", filename);
                        suffix = (char *)&filename[strlen(filename)-4];
                        // cfg
                        if(0==strcmp(suffix, ".cfg")) memcpy(buffer, download_cfg_temp, strlen(download_cfg_temp));
                        // fw
                        else if(0==strcmp(suffix, ".bin")) memcpy(buffer, download_fw_temp, strlen(download_fw_temp));
                        else memcpy(buffer, download_fw_temp, strlen(download_fw_temp));
                        // 匹配校验码
                        server_checksum=decode.checksum;
                        checksum = crc_form_file(download_cache_path, obd_buf, sizeof (obd_buf));
                        //printf("checksum: 0x%08X 0x%08X\n", checksum, server_checksum);
                        if(checksum!=server_checksum)
                        {
                            ret = ERR_CLIENT_DOWN;
                            //break;
                        }
                        fd = open((const char *)buffer, O_RDONLY, 0);
                        if(0 <= fd)
                        {
                            close(fd);
                            //remove(buffer);
                            unlink((const char *)buffer);
                        }
                        rename(download_cache_path, (const char *)buffer);
                        printf("\n\n %s Download Done!\n", buffer); fflush(stdout);
                    }
                    else
                    {
                        decode.down_len = _size;
                        decode.cmd = UPLOAD_DOWNLOAD;
                        decode.data_len = strlen(filename)+1;
                        memcpy(decode.data, filename, decode.data_len);
                        //_load = upload_init(UPLOAD_LOGIN, 0, 0, 0x12345678, "OBD1234567890ABCDEF", "Hello", 5);
                        memset(buffer, 0, sizeof (buffer));
                        len = upload_encode(&decode, buffer, sizeof (buffer));   // user def
                        len = _agree_ofp->encode(buffer, len, obd_buf, sizeof (obd_buf)); // encode
                        if(NULL != csend) csend(client, obd_buf, len); // 发送数据到客户端
                        ret = STATUS_CLIENT_DOWN;
                    }
                }
                else ret = ERR_CLIENT_DOWN;
                break;
            case UPLOAD_QUERY_FW:    // 查询
                printf("userdef Config\n"); fflush(stdout);
                if( ((decode.pack_total<<1)==decode.pack_index) && (decode.data_len>strlen("./upload/*.bin")+1) ) // update
                {
                    decode.pack_index = 0;
                    decode.down_len = FRAME_DATA_LEN;
                    if(decode.down_len>decode.pack_total) decode.down_len=decode.pack_total;
                    decode.cmd = UPLOAD_DOWNLOAD;
                    memset(filename, 0, sizeof (filename));
                    memcpy(filename, decode.data, strlen((const char *)decode.data));
                    decode.data_len = strlen((const char *)decode.data)+1;
                    //_load = upload_init(UPLOAD_LOGIN, 0, 0, 0x12345678, "OBD1234567890ABCDEF", "Hello", 5);
                    memset(buffer, 0, sizeof (buffer));
                    len = upload_encode(&decode, buffer, sizeof (buffer));   // user def
                    len = _agree_ofp->encode(buffer, len, obd_buf, sizeof (obd_buf)); // encode
                    if(NULL != csend) csend(client, obd_buf, len); // 发送数据到客户端
                    ret = STATUS_CLIENT_DOWN;
                    //mkdir("./upload");
                    //rename(filename, "./upload/Download.dat");
                }
                else  ret = STATUS_CLIENT_DONE;
                break;
            default:
                break;
        }
    }
    return ret;
}
static int protocol_shanghai(const struct agreement_ofp* _agree_ofp, const struct general_pack_shanghai* const _pack, const int client, void(*csend)(const int sockfd, const void *buf, const uint16_t len))
{
    const char* ssl_type[] = {
        "0x00: NULL",
        "0x01：数据不加密",
        "0x02：数据经过 RSA 算法加密",
        "0x03：数据经过国密 SM2 算法加密",
        "“0xFE”标识异常，“0xFF”表示无效，其他预留 ",
    };
    int ret = 0;
    const struct general_pack_shanghai* const pack = _pack;
    printf("start: %c%c \n", pack->start[0], pack->start[1]);
    printf("CMD: %d \n", pack->cmd);
    printf("VIM: %s\n", pack->VIN);
    printf("soft_version: %d \n", pack->soft_version);
    printf("ssl: %s \n", ssl_type[pack->ssl&0x3]);
    printf("data_len: %d \n", pack->data_len);
    printf("BCC: %02X \n", pack->BCC);
    fflush(stdout);
    switch(pack->cmd)
    {
#if 0
        case CMD_LOGIN:        // 车辆登入
            printf("车辆登入\n"); fflush(stdout);
            //pack_len = handle_request_login((const struct shanghai_login *const)msg_buf, pack);
            handle_request_login(pack);
            break;
        case CMD_REPORT_REAL:  // 实时信息上报
            printf("实时信息上报\n"); fflush(stdout);
            //handle_report_real((const struct shanghai_report_real *const)msg_buf, pack);
            handle_report_real(pack);
            break;
        case CMD_REPORT_LATER: // 补发信息上报
            printf("补发信息上报\n"); fflush(stdout);
            //handle_report_real((const struct shanghai_report_real *const)msg_buf, pack);
            handle_report_real(pack);
            break;
        case CMD_LOGOUT:       // 车辆登出
            printf("车辆登出\n"); fflush(stdout);
            //pack_len = handle_request_logout((const struct shanghai_logout *const)msg_buf, pack);
            handle_request_logout(pack);
            break;
        case CMD_UTC:          // 终端校时
            printf("终端校时\n"); fflush(stdout);
            break;
#endif
        case CMD_USERDEF:      // 用户自定义
            printf("CMD_USERDEF\n"); fflush(stdout);
            ret = handle_request_userdef(_agree_ofp, pack, client, csend);
            break;
        default:
            printf("default\n"); fflush(stdout);
            break;
    }
    return ret;
}
int decode_client(const struct agreement_ofp* _agree_ofp, const uint8_t pack[], const uint16_t _psize, void* const _msg_buf, const uint16_t _msize, const int client, void(*csend)(const int sockfd, const void *buf, const uint16_t len))
{
    int len = 0;
    int ret = 0;
//    for(len=0; len<40; len++)
//    {
//        printf("\033[1A"); //先回到上一行
//        printf("\033[K");  //清除该行
//    }
    len =_agree_ofp->decode(pack, _psize, _msg_buf, _msize);
    printf("decode_test len : %d \n", len); fflush(stdout);
    if(len<0) return ERR_CLIENT_PACKS;
    switch (_agree_ofp->protocol())
    {
        case PRO_TYPE_CCU:    // CCU
            //
            break;
        case PRO_TYPE_SHH:    // ShangHai
            //printf("协议类型：上海OBD协议 \n"); fflush(stdout);
            ret = protocol_shanghai(_agree_ofp, (const struct general_pack_shanghai* const)_msg_buf, client, csend);
            break;
        default:
            ret = ERR_CLIENT_CMD;
            break;
    }
    return ret;
}





